/*
 * File contenente il modulo Manipolazione/Stampa
 */
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include "strutt_dati.h"
#include "manip_stampa.h"

using namespace std ;

// Inizio definizioni delle funzioni del modulo Manipolazione/Stampa

void inizializza(desc_sequenza &s, const tipo_chiave_t t)
{
    s.elementi = new elem_p[DIM_BASE] ;
    s.dim = DIM_BASE ;
    s.num_elem = 0 ;
    s.ordinata = true ;
    s.tipo_chiave = t ;
}

elem *crea_elem(istream &is)
{
    char parola[MAXLUN] ;

    // lettura chiave e controllo del successo dell'operazione
    if (!(is>>parola)) {
	cerr<<"Errore in lettura chiave"<<endl ;
	return 0 ;
    }

    // creazione elemento
    elem *el = new elem ;

    //inizializzazione
    strcpy(el->chiave, parola) ;
    el->lista_parole = 0 ;

    // lettura parole
    parola_elem *prec = el->lista_parole ;
    while(is>>parola) {         
	
	// creo l'elemento
     	parola_elem *p = new parola_elem ;
     	strcpy(p->parola, parola) ;
     	p->pross = 0 ; // inizializzato opportunamente come ultimo elemento
     
     	// la lista era vuota
     	if (prec == 0)
	el->lista_parole = p ;
     	else 
     	prec->pross = p ;
     	
     	prec = p ;     	
    }

    // resettiamo i flag per evitare problemi in eventuali letture successive
    is.clear() ;
    return el ;
}


/*
 * Dealloca dalla memoria un elemento della lista.
 *
 * Questa funzione è privata, ossia non fa parte dell'interfaccia del modulo.
 */
static void dealloca_elem(elem *p)
{
    
    for (parola_elem *pp = p->lista_parole ; pp != 0 ; ) {
	parola_elem *pross = pp->pross ;
	delete pp ;
	pp = pross ;
    }
    delete p ;
}

void distruggi(desc_sequenza &s)
{
    

    for (int i = 0 ; i < s.num_elem ; i++) {
	
	dealloca_elem(s.elementi[i]) ;
    }

    delete [] s.elementi ;
    s.elementi = 0 ; // per prevenire accessi futuri con puntatori pendenti
    s.num_elem = 0 ;
    s.dim = 0 ;
}

inline int diff(const desc_sequenza &s, const char c1[], const char c2[])
{
    
    return s.tipo_chiave == CHIAVE_NUMERICA ?
	atoi(c1) - atoi(c2) : strcmp(c1, c2) ;
}

void inserisci(desc_sequenza &s, elem *el, ins_modo modo)
{
    assert(el != 0 && s.elementi != 0) ;
    int pos = 0 ;
    
    if (modo == in_coda)
	pos = s.num_elem ;
    else if (modo == in_ordine) {
	if (!s.ordinata) {
	    cout<<"Inserimento in ordine non effettuato, "
		<<"perché la sequenza non è ordinata"<<endl ;
	    return ;
	}
	for ( ; pos < s.num_elem && 
		  diff(s, s.elementi[pos]->chiave, el->chiave) < 0 ;
	      pos++)
	    ;
	
    }

    // se non c'e' piu' spazio riallochiamo l'array
    
    if (s.num_elem == s.dim) {
	D1(cout<<"Riallocazione ..."<<endl) ;
	elem_p *nuovo = new elem_p[s.dim + DIM_BASE] ;
	for (int i = 0 ; i < s.num_elem ; i++)
	    nuovo[i] = s.elementi[i] ;
	delete [] s.elementi ;
	s.elementi = nuovo ;
    }

    for (int i = s.num_elem ; i > pos ; i--)
	s.elementi[i] = s.elementi[i - 1] ;
    
    s.elementi[pos] = el ;
    s.num_elem++ ;
    
    if (modo != in_ordine && s.num_elem > 1)
	s.ordinata = diff(s, s.elementi[0]->chiave, s.elementi[1]->chiave) < 0 
	    && 
	    diff(s, s.elementi[s.num_elem-2]->chiave, 
		 s.elementi[s.num_elem - 1]->chiave) < 0 ;
}

int ric_sequenziale(const desc_sequenza &s, const char c[])
{
    int pos ;
    for (pos = 0 ; pos < s.num_elem ; pos++)
	if (diff(s, s.elementi[pos]->chiave, c) == 0)
	    return pos ;
    return -1 ;
}

int ric_binaria(const desc_sequenza &s, const char c[])
{
    if (s.num_elem == 0)
	return -1 ;
    int a = 0, b = s.num_elem - 1, m = (a+b) / 2 ;
    int d ;
   
    while ((d = diff(s, c, s.elementi[m]->chiave)) != 0) {
	if (d < 0) 
	    b = m - 1 ;
	else 
	    a = m + 1 ;
	
	if (a > b) 
	    return -1 ;
	m = (a+b) / 2;
    }
    return m ;
}

int cerca(const desc_sequenza &s, const char c[])
{
    
    return s.ordinata ? ric_binaria(s, c) : ric_sequenziale(s, c) ;
}

bool elimina(desc_sequenza &s, const char c[])
{
   
    int pos = cerca(s, c) ;
    if (pos == -1)
	return false ;

    dealloca_elem(s.elementi[pos]) ;

    for (int i = pos ; i < s.num_elem - 1 ; i++) {
	
	s.elementi[i] = s.elementi[i+1] ;
    }

    s.num_elem-- ;
    
    return true ;
}

void stampa_elem(const elem &el, ostream &os)
{
    os<<el.chiave ;
    for (parola_elem *p = el.lista_parole ; p != 0 ; p = p->pross) {
	assert(p != 0 && static_cast<int>(strlen(p->parola)) < MAXLUN) ;
	os<<" "<<p->parola ;
    }
    os<<endl ;
}

void stampa(const desc_sequenza &s)
{
    
    for (int i = 0 ; i < s.num_elem ; i++) {
	cout<<"Elemento "<<i<<":"<<endl<<'\t' ;
	
	stampa_elem(*s.elementi[i], cout);
    }
    
    cout<<endl<<"Numero di elementi: "<<s.num_elem<<endl ;
    cout<<"Dimensione array: "<<s.dim<<endl ;
    cout<<(s.ordinata ? "Sequenza ordinata" : "Sequenza non ordinata")<<endl ;
}

// Fine definizioni delle funzioni del modulo Manipolazione/Stampa
